package com.atzly.community.datasource;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidPooledConnection;
import com.atzly.community.datasource.util.ContextHelper;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 动态数据源
 */
public class DynamicDataSource extends AbstractRoutingDataSource  {



    private static final Logger logger = LoggerFactory.getLogger(DynamicDataSource.class);

    private volatile DataSource defaultDataSource;
    private String type;

    public static final Map<String, ArrayDataSource> DYNAMIC_DATA_SOURCE_MAP = new ConcurrentHashMap<>();

    private DataSourceBuilder dataSourceBuilder = new DataSourceBuilder();

    @PostConstruct
    public void postConstruct() {
    }
    /**
     * 根据上下文获取当前线程的数据源name
     * @return
     */
    public String getCurrentDataSourceNameByContext() {
        String contextDataSource = DataSourceContextHolder.getDataSource();
        // 校验数据源是否为空，如果不为空就直接返回
        // 不能从上下文环境取数据源，因为当前线程可能会涉及到多个数据源
        if (StringUtils.isNotEmpty(contextDataSource)) {
            return contextDataSource;
        }
        // TODO
        Long tenantId = null;
        try {
            // 从spring上下文环境中获取租户id
//            tenantId = ContextHelper.getServiceContext().getTenantId();
        } catch (Exception e) {
            // ignore
        }
        // 如果获取到租户id， 如果为空就返回默认的数据源配置name
        if (tenantId == null || tenantId.equals(0L)) {
            return DataSourceConstants.DEFAULT_DATA_SOURCE_NAME;
        }

        String dataSourceName = "";
        // 数据源id
        String dbId = DataSourceConfigManager.getDbIdByTenantId(tenantId);
        // 如果数据源id为空，就获取默认的
        if (StringUtils.isEmpty(dbId)) {
            logger.warn("getDbIdByOrgId return null:{}", tenantId);
            dataSourceName = DataSourceConstants.DEFAULT_DATA_SOURCE_NAME;
        } else {
            // 根据主从配置选择，获取对应的数据源name
            if ("slave".equals(type)) {
                dataSourceName = DataSourceConstants.SLAVE_DYNAMIC_DATA_SOURCE_NAME + DataSourceConstants.SEPARATOR_CHAR + dbId;
            } else {
                dataSourceName = DataSourceConstants.DYNAMIC_DATA_SOURCE_NAME + DataSourceConstants.SEPARATOR_CHAR + dbId;
            }
        }
        return dataSourceName;
    }

    /**
     * 刷新数据源
     * @param dbId
     */
    public void refreshDataSource(String dbId) {
        String dataSourceName = DataSourceConstants.DYNAMIC_DATA_SOURCE_NAME + DataSourceConstants.SEPARATOR_CHAR + dbId;

        if (StringUtils.equals(dbId, DataSourceConfigManager.CLICKHOUSE_DB_ID)) {
            dataSourceName = DataSourceConstants.DEFAULT_CLICKHOUSE_SOURCE_NAME;
        }

        logger.info("正在刷新dbid为：{},数据源名称为{}的数据源",dbId,dataSourceName);

        if (StringUtils.isBlank(dbId) || !StringUtils.isNumeric(dbId)) {
            return;
        }
        ArrayDataSourceConfig dataSourceConfig = DataSourceConfigManager.getArrayDataSourceConfigByDbId(type, dbId);
        ArrayDataSource arrayDataSource = dataSourceBuilder.buildArrayDataSource(dataSourceConfig);
        DYNAMIC_DATA_SOURCE_MAP.put(dataSourceName,arrayDataSource);

    }

    @Override
    protected String determineCurrentLookupKey() {
        String peek = DynamicDataSourceContextHolder.peek();
        MyRouterConfig routerConfig = ((MyRouterConfig) SpringContextUtil.getBean("MyRouterConfig"));
        Boolean enable = routerConfig.getEnable();
        if (!enable) {
            String dbSource = routerConfig.getDbSource();
            logger.info("未开启动态路由，采用指定单路由，路由：{}",dbSource);
            if (StringUtils.isEmpty(dbSource)) {
                logger.error("未配置数据源： mysql.router.db-source");
                throw new RuntimeException("未配置数据源： mysql.router.db-source");
            }
            peek = dbSource;
        }
        if (ContextHelper.getSpecified()) {
            peek = ContextHelper.getDsLocal();
        }
        if (StringUtils.isNotEmpty(peek) && StringUtils.equals(peek,"click")) {
            // 如果使用注解调用click， 则返回ck数据源名称
            return DataSourceConstants.DEFAULT_CLICKHOUSE_SOURCE_NAME;
        }
        if (StringUtils.isNotEmpty(peek) && StringUtils.equals(peek,"doris")) {
            // 如果使用注解调用click， 则返回ck数据源名称
            return DataSourceConstants.DEFAULT_DORIS_SOURCE_NAME;
        }
        if (StringUtils.isNotEmpty(peek) && StringUtils.equals(peek,DataSourceConfigManager.DORIS_DW_DB_ID)) {
            // 如果使用注解调用click， 则返回ck数据源名称
            return DataSourceConstants.DEFAULT_DORIS_DW_SOURCE_NAME;
        }
        if (StringUtils.isNotEmpty(peek) && StringUtils.equals(peek,DataSourceConfigManager.DORIS_HYBIRD_DB_ID)) {
            // 如果使用注解调用click， 则返回ck数据源名称
            return DataSourceConstants.DEFAULT_DORIS_HYBIRD_SOURCE_NAME;
        }
        String dataSourceName = this.getCurrentDataSourceNameByContext();
        return dataSourceName;
    }

    /**
     * 获取数据源
     * @return
     */
    @Override
    protected DataSource determineTargetDataSource() {
        // 判断是否初始化完成
        String dataSourceName = determineCurrentLookupKey();

        // 如果是默认数据源，直接返回默认
        if (DataSourceConstants.DEFAULT_DATA_SOURCE_NAME.equals(dataSourceName)) {
            if (defaultDataSource == null) {
                synchronized (DataSource.class) {
                    if (defaultDataSource == null) {
                        ArrayDataSourceConfig dataSourceConfig = DataSourceConfigManager.getArrayDataSourceConfigByDbId(type, "0");
                        ArrayDataSource arrayDataSource = dataSourceBuilder.buildArrayDataSource(dataSourceConfig);
                        this.defaultDataSource = arrayDataSource.getDataSource();
                        DYNAMIC_DATA_SOURCE_MAP.put(DataSourceConstants.DEFAULT_DATA_SOURCE_NAME,arrayDataSource);
                    }
                }
            }
            ContextHelper.clear();
            return defaultDataSource;
        }

        // 从缓存池中获取数据源
        ArrayDataSource dataSource = DYNAMIC_DATA_SOURCE_MAP.get(dataSourceName);
        if (null == dataSource) {
            dataSource = this.selectDataSource(dataSourceName);
        }
//    try {
//    	DruidDataSource ds = (DruidDataSource)dataSource;
//    	logger.info("DruidDataSource getActiveCount:{}", ds.getActiveCount());
//    	logger.info("DruidDataSource getCloseCount：{}", ds.getCloseCount());
//    	logger.info(" connectCount {}", ds.getConnectCount());
//    	logger.info(" connectErrorCount {}", ds.getConnectErrorCount());
//    	logger.info(" getResetCount:{}", ds.getResetCount());
//    	logger.info(" getPoolingCount:{}", ds.getPoolingCount());
//    }catch(Exception e) {
//    	// logger.error("", e);
//    }
//        logger.info("当前租户：{}，数据源名称：{} ", ContextHelper.getServiceContext().getTenantId(),dataSourceName  );
        ContextHelper.clear();
        return dataSource.getDataSource();
//        return defaultDataSource;
    }

    private synchronized ArrayDataSource selectDataSource(String dataSourceName) {
        ArrayDataSource obj = DYNAMIC_DATA_SOURCE_MAP.get(dataSourceName);
        if (null != obj) {
            return obj;
        }
        ArrayDataSource dataSource = this.getDataSource(dataSourceName);
        if (null != dataSource) {
            DYNAMIC_DATA_SOURCE_MAP.put(dataSourceName, dataSource);
            return dataSource;
        } else {
            throw new RuntimeException("create data source failed:" + dataSourceName);
        }
    }

    private ArrayDataSource getDataSource(String dataSourceName) {
        String dbId = dataSourceName.substring(dataSourceName.lastIndexOf(DataSourceConstants.SEPARATOR_CHAR) + 1);

        if (StringUtils.equals(DataSourceConstants.DEFAULT_CLICKHOUSE_SOURCE_NAME, dataSourceName)) {
            dbId = DataSourceConfigManager.CLICKHOUSE_DB_ID;
        }
        if (StringUtils.equals(DataSourceConstants.DEFAULT_DORIS_DW_SOURCE_NAME, dataSourceName)) {
            dbId = DataSourceConfigManager.DORIS_DW_DB_ID;
        }
        if (StringUtils.equals(DataSourceConstants.DEFAULT_DORIS_HYBIRD_SOURCE_NAME, dataSourceName)) {
            dbId = DataSourceConfigManager.DORIS_HYBIRD_DB_ID;
        }
        if (StringUtils.equals(DataSourceConstants.DEFAULT_DORIS_SOURCE_NAME, dataSourceName)) {
            dbId = DataSourceConfigManager.DORIS_DB_ID;
        }
        ArrayDataSourceConfig dataSourceConfig = DataSourceConfigManager.getArrayDataSourceConfigByDbId(type, dbId);
        ArrayDataSource arrayDataSource = dataSourceBuilder.buildArrayDataSource(dataSourceConfig);

        return arrayDataSource;
    }

    @Override
    public void afterPropertiesSet() {
        /**
         * 该方法重写为空，因为AbstractRoutingDataSource类中会通过此方法将，targetDataSources变量中保存的数据源交给resolvedDefaultDataSource变量
         * 在本方案中动态创建的数据源保存在了本类的targetDataSource变量中。如果不重写该方法为空，会因为targetDataSources变量为空报错
         * 如果仍然想要使用AbstractRoutingDataSource类中的变量保存数据源，则需要在每次数据源变更时，调用此方法来为resolvedDefaultDataSource变量更新
         */
    }

    public DataSource getDefaultDataSource() {
        return defaultDataSource;
    }

    public void setDefaultDataSource(DataSource defaultDataSource) {
        this.defaultDataSource = defaultDataSource;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public DruidPooledConnection getPoolConnection() throws SQLException {
        DataSource determineTargetDataSource = determineTargetDataSource();
        DruidDataSource ds = (DruidDataSource) determineTargetDataSource;
        return ds.getConnection();
    }

//    @Override
    protected DataSource determineDataSource() {
        return determineTargetDataSource();
    }
}
